package org.apache.click.doubleclick.inject;

import org.apache.click.Page;
import org.apache.click.doubleclick.inject.annotation.InjectBean;
import org.apache.commons.lang.Validate;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Responsible for caching scanned {@link org.apache.click.Page} classes and its fields
 * with {@link org.apache.click.doubleclick.inject.annotation.InjectBean} annotation. The
 * caching contains all annotated fields for the page instance and its parent classes.
 *
 * @author Jahid Shohel(development.jsh@gmail.com)
 */
public class Cache {

    //annotation
    private Class<? extends Annotation> annotationClass = InjectBean.class;

    //the cache
    private ConcurrentHashMap<Class<? extends Page>, List<Field>> cache = new ConcurrentHashMap<Class<? extends Page>, List<Field>>();

    /**
     * Returns annotated fields for supplied {@link org.apache.click.Page}.
     *
     * @param page The page instance.
     * @return List of fields annotated with {@link org.apache.click.doubleclick.inject.annotation.InjectBean}.
     *         If the page is null an empty list will be
     * @throws PageCacheException     If page null.
     * @throws PageNotCachedException If the page is not cached yet.
     */
    public List<Field> getAnnotatedFieldsForPage(Page page) throws PageCacheException, PageNotCachedException {
        return getAnnotatedFieldsForPage(page.getClass());
    }

    /**
     * Returns annotated fields for supplied {@link org.apache.click.Page} class.
     *
     * @param clazz The page class.
     * @return List of fields annotated with {@link org.apache.click.doubleclick.inject.annotation.InjectBean}
     * @throws PageCacheException     If page class is null.
     * @throws PageNotCachedException If the page is not cached yet.
     */
    public List<Field> getAnnotatedFieldsForPage(Class<? extends Page> clazz) throws PageCacheException, PageNotCachedException {
        Validate.notNull(clazz, "Page can not be null");
        if (!isCached(clazz)) {
            List<Field> fields = getAnnotatedFields(clazz);
            cache.put(clazz, fields);
            return fields;
        }
        return cache.get(clazz);
    }

    public boolean isCached(Class<? extends Page> clazz) throws PageCacheException {
        Validate.notNull(clazz, "Page can not be null");
        return cache.containsKey(clazz);
    }

    /**
     * Find out all fields which is annotated with {@link org.apache.click.doubleclick.inject.annotation.InjectBean}.
     *
     * @param target The target class to scan.
     * @return A list of fields annotated with {@link org.apache.click.doubleclick.inject.annotation.InjectBean};
     *         otherwise an empty list.
     */
    public List<Field> getAnnotatedFields(Class<?> target) {
        Validate.notNull(target, "The target can not be null");
        List<Field> foundFields = new ArrayList<Field>();

        // scanning until Page.class will not work for other non page classes
        //while (target != null && !target.equals(Page.class)) {
        while (target != null) {
            Field[] fields = target.getDeclaredFields();
            for (Field field : fields) {
                if (field.isAnnotationPresent(annotationClass)) {
                    foundFields.add(field);
                }
            }
            target = target.getSuperclass();
        }
        return foundFields;
    }
}
